 /*
 *
 * Ruby Bytes Extension
 * 
 * Copyright (c) 2008, Giancarlo Bellido
 * 
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 *
 */
#include <ruby.h>

#define RUBY_BYTES_MAX_LEN 16

VALUE m_bytes;

/**
 * Packs object into a string with len length.
 */
static VALUE p_bytes_pack(void* object, int len)
{
	char buffer[RUBY_BYTES_MAX_LEN];
	memcpy(buffer, object, len);
	
	return rb_str_new(buffer, len);
}

/**
 * Pack bytes into a string of length bytes
 */
static VALUE f_bytes_pack(VALUE module, VALUE object, VALUE bytes)
{
	long packlen = NUM2INT(bytes); 
	
	if (packlen > 16)
		rb_raise(rb_eRangeError, "Bytes::pack(): Max string size is 16 bytes.");
	
	switch (rb_type(object)) {
	case T_FIXNUM:
	case T_BIGNUM:
	{
		long long tmp = NUM2INT(object);
		
		return p_bytes_pack((void*)&tmp, packlen);
	}
	case T_STRING:
	{
		int  len;
		char tmp[RUBY_BYTES_MAX_LEN] = { 0 };
		
		len = RSTRING_LEN(object);

		strncpy(tmp, RSTRING_PTR(object), len);
		return p_bytes_pack((void*)tmp, packlen);
	
	}
		
	}
	return Qnil;
}

/**
 * Packs object into a byte.
 */
static VALUE f_bytes_byte(VALUE module, VALUE object)
{
	return f_bytes_pack(module, object, INT2FIX(1));
}

/**
 * Packs object into a word(2 bytes)
 */
static VALUE f_bytes_word(VALUE module, VALUE object)
{
	return f_bytes_pack(module, object, INT2FIX(2));
}

/**
 * Packs object into a dword(4 bytes)
 */
static VALUE f_bytes_dword(VALUE module, VALUE object)
{
	return f_bytes_pack(module, object, INT2FIX(4));
}

/**
 * Packs object into a qword(8 bytes)
 */
static VALUE f_bytes_qword(VALUE module, VALUE object)
{
	return f_bytes_pack(module, object, INT2FIX(8));
}

/**
 * Returns minimum representation of an object in memory.
 */
static VALUE f_bytes_string(VALUE module, VALUE object)
{
	char buffer[RUBY_BYTES_MAX_LEN];
	int  len;
	
	switch (rb_type(object)) {
	case T_FIXNUM:
		len = NUM2INT(object);
		*((unsigned int*)buffer) = len; 
		
		/* Handle 1 bytes */
		if (len < 0x100) len = 1;
		else if(len < 0x10000) len = 2;
		/*else if(x < 0x1000000)len = 3;*/
		else len = 4;
		
		break;	
	case T_BIGNUM:
	{
		*((long long*)buffer) = rb_num2ll(object);
		len = sizeof(long long);
		break;
	}
	case T_FLOAT:
	{
		*((double*)buffer) = rb_num2dbl(object);
		len = sizeof(double);
		break;
	}
	case T_NIL:
	default:
		len = 0;
	}
	
	return rb_str_new(buffer, len);
}

void Init_bytes()
{
	m_bytes = rb_define_module("Bytes");
	rb_define_module_function(m_bytes, "string", 	f_bytes_string, 1);
	rb_define_module_function(m_bytes, "byte", 		f_bytes_byte, 1);
	rb_define_module_function(m_bytes, "word", 		f_bytes_word, 1);
	rb_define_module_function(m_bytes, "dword", 	f_bytes_dword,1);
	rb_define_module_function(m_bytes, "qword",		f_bytes_qword, 1);
	rb_define_module_function(m_bytes, "pack",		f_bytes_pack, 2);		

}
